﻿// (c) Norbert Huffschmid
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.

using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using SpinDotters.Core;

namespace SpinDotters.Widgets
{
    /// <summary>
    /// Counter control
    /// </summary>
    [TemplatePart(Name = "PART_ResetButton", Type = typeof(Button))]
    public partial class Counter : SpinningControlContainer, INotifyPropertyChanged
    {
        /// <summary>
        /// Notifies listeners that a property value has changed.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Event fired when value has changed.
        /// </summary>
        [Category("SpinDotters")]
        public event EventHandler ValueChanged;


        private const int MAX_DIGITS = 6;
        private const int DEFAULT_DIGITS = 4;

        private Button resetButton;
        private int valueLimit;


        /// <summary>
        /// Identifies the Value dependency property.
        /// </summary>
        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register(
                "Value",
                typeof(int),
                typeof(Counter),
                new PropertyMetadata(0, new PropertyChangedCallback(OnValueChanged))
            );

        /// <summary>
        /// Gets or sets the counter value.
        /// </summary>
        [Category("SpinDotters")]
        public int Value
        {
            get { return (int)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        /// <summary>
        /// Identifies the Digits dependency property.
        /// </summary>
        public static readonly DependencyProperty DigitsProperty =
            DependencyProperty.Register(
                "Digits",
                typeof(int),
                typeof(Counter),
                new PropertyMetadata(DEFAULT_DIGITS, new PropertyChangedCallback(OnDigitsChanged))
            );

        /// <summary>
        /// Gets or sets the number of counter digits.
        /// </summary>
        [Category("SpinDotters")]
        public int Digits
        {
            get { return (int)GetValue(DigitsProperty); }

            set
            {
                if ((value < 1) || (value > MAX_DIGITS))
                    throw new ArgumentOutOfRangeException("Invalid number of counter digits");

                SetValue(DigitsProperty, value);
            }
        }

        /// <summary>
        /// Identifies the HasResetButton dependency property.
        /// </summary>
        public static readonly DependencyProperty ResetButtonVisibilityProperty =
            DependencyProperty.Register(
                "ResetButtonVisibility",
                typeof(Visibility),
                typeof(Counter),
                new PropertyMetadata(Visibility.Visible));

        /// <summary>
        /// Gets or sets the visibility of the reset button.
        /// </summary>
        [Category("SpinDotters")]
        public Visibility ResetButtonVisibility
        {
            get { return (Visibility)GetValue(ResetButtonVisibilityProperty); }
            set { SetValue(ResetButtonVisibilityProperty, value); }
        }

        /// <summary>
        /// ReadOnly dependency property.
        /// </summary>
        public static readonly DependencyProperty ReadOnlyProperty =
            DependencyProperty.Register(
                "ReadOnly",
                typeof(bool),
                typeof(Counter),
                null);

        /// <summary>
        /// Indicates whether or not the counter digits are read-only.
        /// </summary>
        public bool ReadOnly
        {
            get { return (bool)GetValue(ReadOnlyProperty); }
            set { SetValue(ReadOnlyProperty, value); }
        }


        /// <summary>
        /// Default constructor
        /// </summary>
        public Counter()
        {
            this.DefaultStyleKey = typeof(Counter);

            this.valueLimit = (int)Math.Pow(10, DEFAULT_DIGITS);

            this.DataContext = this;
        }


        /// <summary>
        /// Reset the counter
        /// </summary>
        public void Reset()
        {
            this.Value = 0;
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            this.resetButton = this.GetTemplateChild("PART_ResetButton") as Button;
            if (this.resetButton != null)
            {
                this.resetButton.Click += new RoutedEventHandler(resetButton_Click);
                this.resetButton.SizeChanged += new SizeChangedEventHandler(Button_SizeChanged);
            }
        }

        protected virtual void OnPropertyChanged(String info)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged(this, new PropertyChangedEventArgs(info));
        }

        protected virtual void OnValueChanged()
        {
            if (this.ValueChanged != null)
                this.ValueChanged(this, EventArgs.Empty);
        }


        private void Button_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            Button button = sender as Button;

            if (button != null)
            {
                // make button square-sized
                if (button.ActualWidth < button.ActualHeight)
                    button.Width = button.ActualHeight;
            }
        }

        private void resetButton_Click(object sender, RoutedEventArgs e)
        {
            this.Reset();
        }

        private int GetDigitValue(int digit)
        {
            int result = ((this.Value / (int)Math.Pow(10, digit)) % 10);
            return result;
        }

        private void SetDigitValue(int digit, int value)
        {
            int upperValue = (this.Value / (int)Math.Pow(10, digit + 1)) * (int)Math.Pow(10, digit + 1);
            int lowerValue = (digit > 0) ? this.Value % (int)Math.Pow(10, digit) : 0;

            this.Value = upperValue + lowerValue + value * (int)Math.Pow(10, digit);
        }

        private Visibility GetVisibility(int digit)
        {
            if (digit >= this.Digits)
                return Visibility.Collapsed;
            else
                return Visibility.Visible;
        }

        private static void OnValueChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            Counter counter = dependencyObject as Counter;

            if (counter.Value >= counter.valueLimit)
            {
                counter.Value = counter.Value % counter.valueLimit;
            }
            else if (counter.Value < 0)
            {
                counter.Value = counter.Value % counter.valueLimit + counter.valueLimit;
            }

            // trigger update of databound digit controls
            counter.OnPropertyChanged("Value5");
            counter.OnPropertyChanged("Value4");
            counter.OnPropertyChanged("Value3");
            counter.OnPropertyChanged("Value2");
            counter.OnPropertyChanged("Value1");
            counter.OnPropertyChanged("Value0");

            counter.OnValueChanged();
        }

        private static void OnDigitsChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            Counter counter = (Counter)dependencyObject;

            counter.valueLimit = (int)Math.Pow(10, counter.Digits);

            // trigger visibility update
            counter.OnPropertyChanged("Visibility5");
            counter.OnPropertyChanged("Visibility4");
            counter.OnPropertyChanged("Visibility3");
            counter.OnPropertyChanged("Visibility2");
            counter.OnPropertyChanged("Visibility1");
            counter.OnPropertyChanged("Visibility0");
        }
    }
}
